home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1 C / WELineLayout.c < prev    next >
Text File  |  1995-05-25  |  17KB  |  602 lines

  1. // { WASTE PROJECT: }
  2. // { Line Layout, Getting and Setting Variables, etc. }
  3.  
  4. // { Copyright © 1993-1994 Marco Piovanelli }
  5. // { All Rights Reserved }
  6.  
  7. // C conversion by Dan Crevier
  8.  
  9. #include "WASTEIntf.h"
  10. #include <Palettes.h>
  11. #include <QDOffscreen.h>
  12. #include <GestaltEqu.h>
  13. #include <Drag.h>
  14.  
  15. pascal void WEStopInlineSession(WEHandle hWE)
  16. {
  17.     TSMDocumentID tsmReference;
  18.  
  19.     tsmReference = (*hWE)->tsmReference;
  20.     if (tsmReference != nil) 
  21.     {
  22.         FixTSMDocument(tsmReference);
  23.     }
  24. }
  25.  
  26. pascal OSErr _WERemoveLine(long lineIndex, WEPtr pWE)
  27. {
  28.     OSErr retval;
  29.     
  30.     // { remove the specified element from the line array }
  31.  
  32.     // { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  33.     retval = _WERemoveSlot((Handle)pWE->hLines, lineIndex, sizeof(LineRec));
  34.  
  35.     // { decrement line count }
  36.     pWE->nLines = pWE->nLines - 1;
  37.     
  38.     return retval;
  39. }
  40.  
  41. pascal OSErr _WEInsertLine(long lineIndex, LineRec *theLine, WEPtr pWE)
  42. {
  43.     // { insert the specified element in the line array }
  44.  
  45.     OSErr err;
  46.  
  47.     // { do the insertion }
  48.     err = _WEInsertSlot((Handle)pWE->hLines, (Ptr)theLine, lineIndex, sizeof(LineRec));
  49.     if (err != noErr) 
  50.     {
  51.         return err;
  52.     }
  53.  
  54.     // { increment line count }
  55.     pWE->nLines = pWE->nLines + 1;
  56.  
  57.     return noErr;
  58. }
  59.  
  60. pascal void _WEBumpOrigin(long lineIndex, long deltaOrigin, WEPtr pWE)
  61. {
  62.     long *pOrigin;
  63.     long nLines;
  64.  
  65.     pOrigin = &((*pWE->hLines)[lineIndex].lineOrigin);
  66.  
  67.     // { loop through the line run array adjusting the lineOrigin fields }
  68.     nLines = pWE->nLines;
  69.     while (lineIndex <= nLines)
  70.     {
  71.         *pOrigin = *pOrigin + deltaOrigin;
  72.         pOrigin = (long *)((long)pOrigin + sizeof(LineRec));
  73.         lineIndex = lineIndex + 1;
  74.     }
  75. }
  76.  
  77. pascal long _WEFindLineBreak(long lineStart, WEHandle hWE)
  78. {
  79.     // { Find where to break the line beginning at lineStart }
  80.     // { the WE record and the text must be already locked }
  81.     // { the current graphics port must be already set up correctly }
  82.  
  83.     WEPtr pWE;
  84.     Ptr pText;
  85.     long offset, breakOffset;
  86.     long textLength;
  87.     long remainingLength;
  88.     long segmentStart, segmentEnd;
  89.     long runIndex;
  90.     WERunInfo runInfo;
  91.     Fixed pixelWidth;
  92.     ScriptCode script, previousScript;
  93.     Boolean isBreak = false;
  94.  
  95.     pWE = *hWE;
  96.     offset = lineStart;
  97.     pText = *pWE->hText + offset;
  98.     remainingLength = pWE->textLength - offset;
  99.  
  100.     // { find the style run index corresponding to the first segment on this line }
  101.     runIndex = _WEOffsetToRun(offset, hWE);
  102.  
  103.     // { initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity }
  104.     pixelWidth = BSL(pWE->destRect.right - pWE->destRect.left, 16);
  105.  
  106.     // { STYLE SEGMENT LOOP }
  107.     do
  108.     {
  109.  
  110.         // { get style run information for the current style run }
  111.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  112.         runIndex = runIndex + 1;
  113.  
  114.         // { set text attributes in the graphics port }
  115.         TextFont(runInfo.runAttrs.runStyle.tsFont);
  116.         TextFace(runInfo.runAttrs.runStyle.tsFace);
  117.         TextSize(runInfo.runAttrs.runStyle.tsSize);
  118.  
  119.         // { if we're handling multiscript text, keep track of script boundaries }
  120.         if (BTST(pWE->flags, weFNonRoman))
  121.         { 
  122.             // { what is the script for this segment? }
  123.             script = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  124.  
  125.             // { have we crossed a script run boundary in the middle of a line? }
  126.             if ((runInfo.runStart > offset) && (script != previousScript))
  127.             {
  128.                 // { leave behind the all previous segments on this line }
  129.                 offset = runInfo.runStart;
  130.                 pText = *pWE->hText + offset;
  131.                 remainingLength = pWE->textLength - offset;
  132.             }
  133.             previousScript = script;
  134.         } // { if non-Roman }
  135.  
  136.         // { we'll pass textLength as the second parameter to the line break hook }
  137.         // { although this parameter is declared as a long, StyledLineBreak uses only }
  138.         // { the low word, so make sure it doesn't trespass the 32,767 byte threshold! }
  139.         textLength = _WEPinInRange(remainingLength, 0, SHRT_MAX);
  140.  
  141.         // { calculate segmentStart and segmentEnd relative to offset }
  142.         segmentStart = _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  143.         segmentEnd = _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  144.  
  145.         // { set breakOffset to a non-zero value for the first script run on the line, }
  146.         // { set it to zero for all subsequent script runs }
  147.         breakOffset = (offset == lineStart);
  148.  
  149.         if (runInfo.runAttrs.runStyle.tsObject != kNullObject)
  150.         {
  151.             // { EMBEDDED OBJECT }
  152.             // { subtract object width from pixelWidth }
  153.             pixelWidth = pixelWidth - 
  154.                 BSL((*(WEObjectDescHandle)(runInfo.runAttrs.runStyle.tsObject))->objectSize.h,
  155.                 16);
  156.             
  157.             // { stop looping if pixelWidth has gone negative }
  158.             isBreak = (pixelWidth < 0);
  159.  
  160.             if (isBreak)
  161.                 breakOffset = segmentStart; // { break line before the object }
  162.             else
  163.                 breakOffset = segmentEnd;    // { break line after the object }
  164.         }
  165.         else
  166.         {
  167.             // { REGULAR TEXT }
  168. #ifdef WASTE_DEBUG
  169.             _WEAssert(pWE->lineBreakHook != NULL, "\pMissing LineBreak Hook");
  170. #endif
  171.             isBreak = (CallWELineBreakProc(pText, textLength, segmentStart,
  172.                         segmentEnd, &pixelWidth, &breakOffset, hWE,
  173.                         (WELineBreakUPP)pWE->lineBreakHook) != smBreakOverflow);
  174.         }
  175.  
  176.         // { break the line anyway when we reach the end of the text }
  177.         if (segmentEnd >= remainingLength)
  178.             isBreak = true;
  179.  
  180.     } while (!isBreak);
  181.  
  182.     // { return the offset from lineStart to the break point }
  183.     return (offset - lineStart) + breakOffset;
  184. }
  185.  
  186. pascal void _WECalcHeights(long rangeStart, long rangeEnd, short *lineAscent, short *lineDescent,
  187.         WEHandle hWE)
  188. {
  189.     // { Find the maximum ascent and descent values between rangeStart and rangeEnd }
  190.     // { the WE record must be already locked }
  191.     // { the current graphics port must be already set up correctly }
  192.  
  193.     long runIndex;
  194.     WERunInfo runInfo;
  195.     short runAscent, runDescent;
  196.  
  197.     *lineAscent = 1;
  198.     *lineDescent = 1;
  199.  
  200.     // { find the style run index corresponding to the first segment on this line }
  201.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  202.  
  203.     // { STYLE SEGMENT LOOP }
  204.     do
  205.     {
  206.         // { get style run information for the current style run }
  207.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  208.         runIndex = runIndex + 1;
  209.  
  210.         // { calculate ascent and descent (actually, descent + leading) values for this style run }
  211.         if (runInfo.runAttrs.runStyle.tsObject != kNullObject)
  212.         {
  213.             // { EMBEDDED OBJECT }
  214.             runAscent = (*(WEObjectDescHandle)(runInfo.runAttrs.runStyle.tsObject))->objectSize.v;
  215.             runDescent = 0;
  216.         }
  217.         else
  218.         {
  219.             // { REGULAR TEXT }
  220.             runAscent = runInfo.runAttrs.runAscent;
  221.             runDescent = runInfo.runAttrs.runHeight - runAscent;
  222.         }
  223.         
  224.         // { save the maximum values in lineAscent and lineDescent }
  225.         if (runAscent > *lineAscent) 
  226.         {
  227.             *lineAscent = runAscent;
  228.         }
  229.         if (runDescent > *lineDescent) 
  230.         {
  231.             *lineDescent = runDescent;
  232.         }
  233.         
  234.         // { keep looping until we reach rangeEnd }
  235.     } while (runInfo.runEnd < rangeEnd);
  236. }
  237.  
  238. pascal OSErr _WERecalBreaks(long *startLine, long *endLine, WEHandle hWE)
  239. {
  240.     // { Recalculates line breaks, line heights and ascents for all the text or for a portion of it. }
  241.     // { On entry, startLine and endLine define a range of lines to recalculate. }
  242.     // { On exit, startLine to endLine defines the range of lines actually recalculated }
  243.     // { the WE record must already be locked }
  244.  
  245.     WEPtr pWE;
  246.     LinePtr pLine;
  247.     LineRec lineInfo, oldLineInfo;
  248.     long lineIndex;
  249.     long recalThreshold;
  250.     long lineOffset;
  251.     short lineAscent, lineDescent;
  252.     Boolean saveTextLock;
  253.     QDEnvironment saveEnvironment;
  254.     OSErr err;
  255.     OSErr retval;
  256.  
  257.     retval = noErr;
  258.     pWE = *hWE;
  259.  
  260.     // { lock the text }
  261.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  262.  
  263.     // { find the character offset that must be necessarily reached before we can }
  264.     // { even consider the possibility of stopping the recalculation process }
  265.     // { this offset, recalThreshold, is the last character on endLine _before_ recalculation }
  266.     lineIndex = _WEPinInRange(*endLine, 0, pWE->nLines - 1);
  267.     recalThreshold = (*pWE->hLines)[lineIndex + 1].lineStart;
  268.  
  269.     // { we start recalculating line breaks from the line actually _preceding_ startLine, }
  270.     // { since editing startLine may cause part of its text to fit on the preceding line }
  271.     lineIndex = _WEPinInRange(*startLine - 1, 0, pWE->nLines - 1);
  272.  
  273.     // { find where in the text recalculation should begin }
  274.     lineInfo = (*pWE->hLines)[lineIndex];
  275.  
  276.     // { save the QuickDraw environment }
  277.     _WESaveQDEnvironment(pWE->port, false, &saveEnvironment);
  278.  
  279.     // { MAIN LINE BREAKING LOOP }
  280.     do
  281.     {
  282.         // { find where to break the current line }
  283.         lineOffset = _WEFindLineBreak(lineInfo.lineStart, hWE);
  284.  
  285.         // { make sure we advance at least by one character (unless we reached the end of text) }
  286.         if (lineOffset <= 0)
  287.         {
  288.             if (lineInfo.lineStart < pWE->textLength)
  289.                 lineOffset = 1;
  290.         }
  291.         
  292.         // { calculate ascent and descent values for this line }
  293.         _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, &lineAscent, &lineDescent, hWE);
  294.  
  295.         // { save the maximum line ascent for this line in the line array }
  296.         pLine = &(*pWE->hLines)[lineIndex];
  297.         pLine->lineAscent = lineAscent;
  298.  
  299.         // { increment counters (go to the next line array entry) }
  300.         lineIndex = lineIndex + 1;
  301.         lineInfo.lineStart = lineInfo.lineStart + lineOffset;
  302.         lineInfo.lineOrigin = lineInfo.lineOrigin + (lineAscent + lineDescent);
  303.         pLine++;
  304.         
  305.         // { compare the newly calculated line start with the old value }
  306.         // { if the new line start comes before the old line start, insert a new element }
  307.         oldLineInfo = *pLine;
  308.         if ((lineIndex > pWE->nLines) || (lineInfo.lineStart < oldLineInfo.lineStart)) 
  309.         {
  310.             err = _WEInsertLine(lineIndex, &lineInfo, pWE);
  311.  
  312.             // { clean up and exit if we ran out of memory }
  313.             if (err != noErr) 
  314.             {
  315.                 retval = err;
  316.                 goto cleanup;
  317.             }
  318.         }
  319.         else
  320.         {
  321.             // { overwrite the old element }
  322.             pLine->lineStart = lineInfo.lineStart;
  323.             pLine->lineOrigin = lineInfo.lineOrigin;
  324.  
  325.             // { remove all further elements which have a lineStart field }
  326.             // { less than or equal to the current one }
  327.             while((lineIndex + 1 <= pWE->nLines) && 
  328.                 (lineInfo.lineStart >= (*pWE->hLines)[lineIndex + 1].lineStart))
  329.             {
  330.                 err = _WERemoveLine(lineIndex + 1, pWE);
  331.             }
  332.  
  333.             // { if the new line start is the same as the old one... }
  334.             if (lineInfo.lineStart == oldLineInfo.lineStart) 
  335.             {
  336.                 // { ...and recalThreshold has been reached, we can stop recalculating line breaks }
  337.                 if (lineInfo.lineStart >= recalThreshold) 
  338.                 {
  339.                     // { although line breaks need not be changed from lineIndex on, }
  340.                     // { the lineOrigin fields may need to be changed }
  341.                     if (lineInfo.lineOrigin != oldLineInfo.lineOrigin) 
  342.                     {
  343.                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  344.                     }
  345.                     
  346.                     // { exit from the line breaking loop }
  347.                     goto cleanup;
  348.                 }
  349.             }
  350.             else
  351.             {
  352.                 // { otherwise, the new line start comes after the old line start... }
  353.                 // { if the current line is the one preceding startLine, warn our caller about this }
  354.                 if ((lineIndex > 0) && (lineIndex == *startLine)) 
  355.                 {
  356.                     *startLine = lineIndex - 1;
  357.                 }
  358.             }
  359.         }
  360.     } while(lineInfo.lineStart < pWE->textLength);
  361.  
  362. cleanup:
  363.     // { set destRect.bottom to destRect.top + total text height }
  364.     pWE->destRect.bottom = pWE->destRect.top + WEGetHeight(0, pWE->nLines, hWE);
  365.  
  366.     // { quirk: if the last character in the text is a carriage return, the caret appears }
  367.     // { below the last line, so in this case we need to add the extra height to destRect.bottom }
  368.     if (WEGetChar(pWE->textLength - 1, hWE) == '\r')
  369.     {
  370.         pWE->destRect.bottom = pWE->destRect.bottom + 
  371.                                     WEGetHeight(pWE->nLines - 1, pWE->nLines, hWE);
  372.     }
  373.     
  374.     // { return through endLine the index of the last line affected by recalculation }
  375.     *endLine = lineIndex - 1;
  376.  
  377.     // { make sure startLine isn't greater than endLine }
  378.     if (*startLine > *endLine) 
  379.     {
  380.         *startLine = *endLine;
  381.     }
  382.     
  383.     // { unlock the text }
  384.     _WESetHandleLock(pWE->hText, saveTextLock);
  385.  
  386.     // { restore the QuickDraw environment }
  387.     _WERestoreQDEnvironment(&saveEnvironment);
  388.     return retval;
  389. }
  390.  
  391. Boolean SLCalcSlop(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment, long segmentStart,
  392.     long segmentLength, JustStyleCode styleRunPosition, void *callbackData);
  393.  
  394.  
  395. Boolean SLCalcSlop(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment, long segmentStart,
  396.     long segmentLength, JustStyleCode styleRunPosition, void *callbackData)
  397. {
  398.     Boolean isEndOfLine;
  399.     Boolean retval;
  400.     struct SLCalcSlopData *p = (struct SLCalcSlopData *) callbackData;
  401.     short segmentWidth;
  402.     Fixed segmentProportion;
  403.  
  404.     retval = false;                // { keep looping }
  405.  
  406.     // { see if this text segment ends with a carriage return, or if we've reached the }
  407.     // { end of the text (in which case we don't want any justification to take place) }
  408.     isEndOfLine = (segmentStart + segmentLength >= p->pWE->textLength) ||
  409.                     ( *((Ptr)pSegment + segmentLength - 1) == kEOL);
  410.  
  411.     // { if this is the first segment on the line, reset line totals }
  412.     if (styleRunPosition <= leftStyleRun) 
  413.     {
  414.         p->totalSlop = p->lineWidth;
  415.         p->totalProportion = 0;
  416.     }
  417.     
  418.     if (pAttrs->runStyle.tsObject != kNullObject)
  419.     {
  420.         // { EMBEDDED OBJECT }
  421.         // { segment width is just object width; no extra space can be applied for justification }
  422.         segmentWidth = (*(WEObjectDescHandle)(pAttrs->runStyle.tsObject))->objectSize.h;
  423.         segmentProportion = 0;
  424.     }
  425.     else
  426.     {
  427.         //{ REGULAR TEXT }
  428.  
  429.         // { if this is the last segment on the line, strip trailing spaces }
  430.         if (!(styleRunPosition & 1)) 
  431.         {
  432.             segmentLength = VisibleLength(pSegment, segmentLength);
  433.         }
  434.         // { measure this segment }
  435.         segmentWidth = TextWidth(pSegment, 0, segmentLength);
  436.  
  437.         // { calculate the proportion of extra space to apply to this text segment }
  438.         segmentProportion = PortionLine(pSegment, segmentLength,
  439.             styleRunPosition, kOneToOneScaling, kOneToOneScaling);
  440.     }
  441.     
  442.     // keep track of line totals
  443.     p->totalSlop = p->totalSlop - segmentWidth;
  444.     p->totalProportion = p->totalProportion + segmentProportion;
  445.  
  446.     // { if this is the last segment on the line, save values in the line array }
  447.     if (!(styleRunPosition & 1)) 
  448.     {
  449.         // { make sure slop is non-negative }
  450.         if (p->totalSlop < 0)
  451.             p->totalSlop = 0;
  452.         pLine->lineSlop = p->totalSlop;
  453.  
  454.         if (isEndOfLine) 
  455.         {
  456.             pLine->lineJustAmount = 0;
  457.         }
  458.         else
  459.         {
  460.                 pLine->lineJustAmount = FixDiv(BSL(p->totalSlop, 16), p->totalProportion);
  461.         }
  462.     }
  463.     return retval;
  464. }
  465.  
  466.  
  467. pascal void _WERecalSlops(long firstLine, long lastLine, WEHandle hWE)
  468. {
  469.     // { Calculates the lineSlop and lineJustAmount fields }
  470.     // { of the line array for the specified lines }
  471.  
  472.     WEPtr pWE;
  473.     short lineWidth;
  474.     struct SLCalcSlopData callbackData;
  475.  
  476. #ifdef WESMARTRECALCSLOP
  477.     // we only need to bother if the user isn't using left justification
  478.     if ((*hWE)->alignment == weFlushLeft) return;
  479. #endif
  480.  
  481.     pWE = *hWE;
  482.     lineWidth = pWE->destRect.right - pWE->destRect.left;
  483.  
  484.     // { calculate slop and normalized slop proportion for all lines }
  485.     callbackData.lineWidth = lineWidth;
  486.     callbackData.pWE = pWE;
  487.     _WESegmentLoop(firstLine, lastLine, SLCalcSlop, (void *) &callbackData, hWE);
  488. }
  489.  
  490. pascal OSErr WEUseText(Handle text, WEHandle hWE)
  491. {
  492.     WEPtr pWE;
  493.     long textLength;
  494.     Boolean saveWELock;
  495.  
  496.     // { lock the WE record }
  497.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  498.     pWE = *hWE;
  499.  
  500.     // { install the text }
  501.     _WEForgetHandle(&pWE->hText);
  502.     pWE->hText = text;
  503.     textLength = GetHandleSize(text);
  504.     pWE->textLength = textLength;
  505.     (*pWE->hRuns)[pWE->nRuns].runStart = textLength + 1;
  506.     (*pWE->hLines)[pWE->nLines].lineStart = textLength;
  507.     
  508.     // { unlock the WE record }
  509.     _WESetHandleLock((Handle)hWE, saveWELock);
  510.  
  511.     return noErr;
  512. }
  513.  
  514. pascal char WEGetAlignment(WEHandle hWE)
  515. {
  516.     return (*hWE)->alignment;
  517. }
  518.  
  519. pascal void WEGetSelection(long *selStart, long *selEnd, WEHandle hWE)
  520. {
  521.     WEPtr pWE;
  522.  
  523.     pWE = *hWE;
  524.     *selStart = pWE->selStart;
  525.     *selEnd = pWE->selEnd;
  526. }
  527.  
  528. pascal void WESetDestRect(LongRect *destRect, WEHandle hWE)
  529. {
  530.     (*hWE)->destRect = *destRect;
  531. }
  532.  
  533. pascal void WEGetDestRect(LongRect *destRect, WEHandle hWE)
  534. {
  535.     *destRect = (*hWE)->destRect;
  536. }
  537.  
  538. pascal void WESetViewRect(LongRect *viewRect, WEHandle hWE)
  539. {
  540.     WEPtr pWE;
  541.     Rect r;
  542.     
  543.     pWE = *hWE;
  544.     pWE->viewRect = *viewRect;
  545.  
  546.     // { keep the viewRgn in sync with the view rectangle }
  547.     WELongRectToRect(viewRect, &r);
  548.     RectRgn(pWE->viewRgn, &r);
  549. }
  550.  
  551. pascal void WEGetViewRect(LongRect *viewRect, WEHandle hWE)
  552. {
  553.     *viewRect = (*hWE)->viewRect;
  554. }
  555.  
  556. pascal long WEGetTextLength(WEHandle hWE)
  557. {
  558.     return (*hWE)->textLength;
  559. }
  560.  
  561. pascal long WECountLines(WEHandle hWE)
  562. {
  563.     return (*hWE)->nLines;
  564. }
  565.  
  566. pascal long WEGetHeight(long startLine, long endLine, WEHandle hWE)
  567. {
  568.     WEPtr pWE;
  569.     LineArrayPtr pLines;
  570.     long nLines;
  571.  
  572.     pWE = *hWE;
  573.     pLines = *pWE->hLines;
  574.     nLines = pWE->nLines;
  575.     startLine = _WEPinInRange(startLine, 0, nLines);
  576.     endLine = _WEPinInRange(endLine, 0, nLines);
  577.     _WEReorder(&startLine, &endLine);
  578.     return pLines[endLine].lineOrigin - pLines[startLine].lineOrigin;
  579. }
  580.  
  581. pascal Handle WEGetText(WEHandle hWE)
  582. {
  583.     return (*hWE)->hText;
  584. }
  585.  
  586. pascal char WEGetChar(long offset, WEHandle hWE)
  587. {
  588.     WEPtr pWE;
  589.  
  590.     pWE = *hWE;
  591.  
  592.     // { sanity check: make sure offset is withing allowed bounds }
  593.     if ((offset < 0) || (offset >= pWE->textLength))
  594.     {
  595.         return 0;
  596.     }
  597.  
  598.     // { get the specified character (actually, byte) }
  599.     return (*pWE->hText)[offset];
  600. }
  601.  
  602.